/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package io.github.yangziwen.zyftp.util;

/**
 * <strong>Internal class, do not use directly.</strong>
 *
 * This is a simplified regular character mattching class. Supports *?^[]-
 * pattern characters.
 *
 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
 */
public class RegularExpr {

	private char[] pattern;

	/**
	 * Constructor.
	 *
	 * @param pattern
	 *			regular expression
	 */
	public RegularExpr(String pattern) {
		this.pattern = pattern.toCharArray();
	}

	/**
	 * Compare string with a regular expression.
	 */
	public boolean isMatch(String name) {

		// common pattern - *
		if ((pattern.length == 1) && (pattern[0] == '*')) {
			return true;
		}

		return isMatch(name.toCharArray(), 0, 0);
	}

	/**
	 * Is a match?
	 */
	private boolean isMatch(char[] strName, int strIndex, int patternIndex) {

		while (true) {

			// no more pattern characters
			// if no more strName characters - return true
			if (patternIndex >= pattern.length) {
				return strIndex == strName.length;
			}

			char pc = pattern[patternIndex++];
			switch (pc) {

			// Match a single character in the range
			// If no more strName character - return false
			case '[':

				// no more string character - returns false
				// example : pattern = ab[^c] and string = ab
				if (strIndex >= strName.length) {
					return false;
				}

				char fc = strName[strIndex++];
				char lastc = 0;
				boolean bMatch = false;
				boolean bNegete = false;
				boolean bFirst = true;

				while (true) {

					// single character match
					// no more pattern character - error condition.
					if (patternIndex >= pattern.length) {
						return false;
					}
					pc = pattern[patternIndex++];

					// end character - break out the loop
					// if end bracket is the first character - always a match.
					// example pattern - [], [^]
					if (pc == ']') {
						if (bFirst) {
							bMatch = true;
						}
						break;
					}

					// if already matched - just read the rest till we get ']'.
					if (bMatch) {
						continue;
					}

					// if the first character is the negete
					// character - inverse the matching condition
					if ((pc == '^') && bFirst) {
						bNegete = true;
						continue;
					}
					bFirst = false;

					// '-' range check
					if (pc == '-') {

						// pattern string is [a- error condition.
						if (patternIndex >= pattern.length) {
							return false;
						}

						// read the high range character and compare.
						pc = pattern[patternIndex++];
						bMatch = (fc >= lastc) && (fc <= pc);
						lastc = pc;
					}

					// Single character match check. It might also be the
					// low range character.
					else {
						lastc = pc;
						bMatch = (pc == fc);
					}
				}

				// no match - return false.
				if (bNegete) {
					if (bMatch) {
						return false;
					}
				} else {
					if (!bMatch) {
						return false;
					}
				}
				break;

			// * - skip zero or more characters
			// No more pattern character - return true
			// Increment strIndex till the rest of the pattern matches.
			case '*':

				// no more string character remaining - returns true
				if (patternIndex >= pattern.length) {
					return true;
				}

				// compare rest of the string
				do {
					if (isMatch(strName, strIndex++, patternIndex)) {
						return true;
					}
				} while (strIndex < strName.length);

				// Example pattern is (a*b) and the string is (adfdc).
				return false;

				// ? - skip one character - increment strIndex.
				// If no more strName character - return false.
			case '?':

				// already at the end - no more character - returns false
				if (strIndex >= strName.length) {
					return false;
				}
				strIndex++;
				break;

			// match character.
			default:

				// already at the end - no match
				if (strIndex >= strName.length) {
					return false;
				}

				// the characters are not equal - no match
				if (strName[strIndex++] != pc) {
					return false;
				}
				break;
			}
		}
	}

}
